package eu.jba.denshijisho.dao;

import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import eu.jba.denshijisho.R;
import eu.jba.denshijisho.util.Util;
import eu.jba.denshijisho.util.WithId;

public abstract class AbstractDao<T extends WithId> {

	static final String TAG = "AbstractDao";

	private final Context context;
	private DatabaseHelper dbHelper;
	private SQLiteDatabase db;

	public AbstractDao(Context ctx) {
		this.context = ctx;
		dbHelper = new DatabaseHelper(context, getCreateResource(),
				getUpdateResource());
	}

	private static class DatabaseHelper extends SQLiteOpenHelper {
		private Context context;

		DatabaseHelper(Context context, int createResource, int updateResource) {
			super(context, context.getResources().getString(
					R.database.DATABASE_NAME), null, context.getResources()
					.getInteger(R.database.DATABASE_VERSION));
			this.context = context;
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			try {
				for (int createScript : DatabaseHelperUtil.CREATE_SCRIPT) {
					db.execSQL(Util.readRawFile(context, createScript));
				}
				db.execSQL(Util.readRawFile(context, R.raw.insert_script));
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
			Log.d(TAG, "Upgrading database from version " + oldVersion + " to "
					+ newVersion + ", which will destroy all old data");
			for (int updateScript : DatabaseHelperUtil.UPDATE_SCRIPT) {
				db.execSQL(Util.readRawFile(context, updateScript));
			}
			onCreate(db);
		}
	}

	public AbstractDao<T> open() throws SQLException {
		db = dbHelper.getWritableDatabase();
		return this;
	}

	public void close() {
		dbHelper.close();
	}

	protected abstract int getCreateResource();

	protected abstract int getUpdateResource();

	protected abstract String getTableName();

	protected abstract ContentValues getInitialValues(T object);

	protected abstract String getRowIdColumnName();

	protected abstract String[] getColumns();

	protected abstract T convertTo(Cursor cursor);

	public List<T> runRawQuery( String query ){
		Cursor cursor = null;
		try {
			cursor = db.rawQuery(query, null);
			final List<T> list = new ArrayList<T>();
			while (cursor.moveToNext()) {
				list.add(convertTo(cursor));
			}
			return list;
		} finally {
			if (cursor != null) {
				cursor.close();
			}
		}

	}
	
	public long insert(T object) {
		return db.insert(getTableName(), null, getInitialValues(object));
	}

	public boolean delete(long rowId) {
		return (db.delete(getTableName(), getRowIdColumnName() + " = " + rowId,
				null)) > 0;
	}

	public List<T> getAll() {
		Cursor cursor = null;
		try {
			cursor = db.query(getTableName(), getColumns(), null, null, null,
					null, null);
			final List<T> list = new ArrayList<T>();
			while (cursor.moveToNext()) {
				list.add(convertTo(cursor));
			}
			return list;
		} finally {
			if (cursor != null) {
				cursor.close();
			}
		}
	}
	
	public T get(long rowId) {
		Cursor cursor = null;
		try {
			cursor = db.query(true, getTableName(), getColumns(),
					getRowIdColumnName() + " = " + rowId, null, null, null,
					null, null);
			if (cursor != null) {
				cursor.moveToFirst();
			}
			return convertTo(cursor);
		} finally {
			if (cursor != null) {
				cursor.close();
			}
		}
	}

	public boolean update(T object) {
		return db.update(getTableName(), getInitialValues(object),
				getRowIdColumnName() + " = " + object.getId(), null) > 0;
	}

	protected Context getContext() {
		return context;
	}

	protected SQLiteDatabase getDb() {
		return db;
	}
}
